package day29.ioFile;

import java.io.File;
import java.io.FileFilter;

/**
 * 检查D盘下所有.docx后缀的文件
 * 该程序会递归遍历D盘的所有目录和子目录，查找并显示所有.docx文件的信息
 * 
 * 程序功能说明：
 * 1. 从D盘根目录开始遍历所有子目录
 * 2. 查找所有以.docx为扩展名的文件（Microsoft Word文档）
 * 3. 显示找到的每个.docx文件的名称和大小信息
 * 4. 以递归方式遍历所有层级的子目录
 */
public class CheckDocxFiles {
    
    /**
     * 程序入口点
     * main方法是Java程序的执行起点，JVM会首先调用此方法开始执行程序
     * 
     * 方法执行流程：
     * 1. 定义要搜索的路径（D盘根目录）
     * 2. 创建File对象表示该路径
     * 3. 检查路径是否存在
     * 4. 调用递归方法搜索.docx文件
     * 
     * @param args 命令行参数，从命令行传递给程序的字符串数组
     *             在此程序中未使用，但作为main方法的标准参数保留
     */
    public static void main(String[] args) {
        // 指定D盘路径，使用正斜杠"/"作为路径分隔符以确保跨平台兼容性
        // 在Windows系统中，也可以使用反斜杠"\\", 但正斜杠更通用
        String Path = "D:/";
        
        // 创建File对象，File类是java.io包中的一个类，用于表示文件和目录路径
        // File对象本身不直接操作文件内容，而是用于文件系统操作（如检查文件是否存在、获取文件属性等）
        File path = new File(Path);
        
        // 检查指定路径在文件系统中是否存在
        // exists()方法返回boolean值，如果文件或目录存在则返回true，否则返回false
        // 这是一种防御性编程实践，确保后续操作的目标路径是有效的
        if (!path.exists()) {
            // 如果路径不存在，向标准输出流打印错误信息并立即退出main方法
            // return语句在void方法中用于提前结束方法执行
            System.out.println("D盘不存在！");
            return;
        }
        
        // 向标准输出流打印提示信息，告知用户程序开始执行搜索操作
        // System.out.println()是Java中最常用的输出方法之一
        System.out.println("开始检查D盘下的所有.docx文件...");
        
        // 调用递归方法搜索.docx文件，传入根目录File对象作为起始搜索点
        // 这将触发递归搜索过程，遍历整个D盘目录树
        searchDocxFiles(path);
    }
    
    /**
     * 递归搜索.docx文件的核心方法
     * 该方法会遍历指定目录下的所有文件和子目录，查找.docx文件并显示其信息
     * 
     * 递归原理说明：
     * 1. 首先在当前目录中查找所有.docx文件并显示信息
     * 2. 然后获取当前目录下的所有子目录
     * 3. 对每个子目录递归调用自身方法，实现深度遍历
     * 4. 当没有更多子目录时递归终止
     * 
     * @param directory 要搜索的目录File对象
     *                  该参数表示当前正在处理的目录路径
     */
    public static void searchDocxFiles(File directory) {
        // 创建文件过滤器，只查找.docx文件
        // FileFilter是一个接口，用于过滤文件列表
        // 通过实现accept方法定义过滤条件，返回true表示接受该文件，false表示拒绝
        FileFilter docxFilter = new FileFilter() {
            /**
             * 确定是否接受指定的文件
             * 该方法是FileFilter接口的唯一抽象方法，必须实现
             * 
             * 过滤条件：
             * 1. 必须是普通文件（而不是目录）
             * 2. 文件名必须以.docx结尾（不区分大小写）
             * 
             * @param file 要检查的File对象
             * @return 如果文件满足条件返回true，否则返回false
             */
            @Override
            public boolean accept(File file) {
                // 检查文件是否为普通文件且以.docx结尾（不区分大小写）
                // file.isFile()方法用于检查File对象是否表示一个普通文件（而非目录或其他类型文件）
                // file.getName()方法返回文件名（包含扩展名）
                // toLowerCase()方法将文件名转换为小写以实现大小写不敏感的比较
                // endsWith(".docx")方法检查字符串是否以指定后缀结尾
                return file.isFile() && file.getName().toLowerCase().endsWith(".docx");
            }
        };
        
        // 查找当前目录下的所有.docx文件
        // listFiles方法会返回满足过滤条件的文件数组
        // 该方法接受一个FileFilter参数，只返回满足过滤条件的文件
        // 如果目录无法读取或发生I/O错误，该方法可能返回null
        File[] docxFiles = directory.listFiles(docxFilter);
        
        // 检查listFiles方法的返回结果是否有效且包含至少一个文件
        // 首先检查docxFiles不为null（避免NullPointerException）
        // 然后检查数组长度是否大于0（确保有文件匹配过滤条件）
        if (docxFiles != null && docxFiles.length > 0) {
            // 使用getAbsolutePath()方法获取目录的完整绝对路径
            // 打印提示信息，显示在哪个目录中找到了.docx文件
            System.out.println("在目录 " + directory.getAbsolutePath() + " 中找到以下.docx文件:");
            
            // 使用增强for循环（for-each循环）遍历所有找到的.docx文件
            // 这种循环语法适用于遍历数组或实现了Iterable接口的集合
            // File file : docxFiles 表示每次循环将数组中的一个元素赋值给file变量
            for (File file : docxFiles) {
                // 打印每个找到的.docx文件的名称和大小信息
                // file.getName()返回文件名（不包含路径）
                // file.length()返回文件大小（以字节为单位）
                System.out.println("  " + file.getName() + " (大小: " + file.length() + " 字节)");
            }
        }
        
        // 递归搜索子目录
        // 使用方法引用File::isDirectory作为过滤条件，只获取目录
        // listFiles方法也可以接受一个方法引用作为过滤条件
        // File::isDirectory是File类的实例方法引用，用于检查File对象是否表示一个目录
        File[] subDirectories = directory.listFiles(File::isDirectory);
        
        // 检查子目录数组是否有效（不为null）
        // 不需要检查长度是否大于0，因为即使没有子目录，for循环也不会执行
        if (subDirectories != null) {
            // 遍历所有子目录并递归调用searchDocxFiles方法
            // 对每个子目录执行相同的操作，实现深度优先搜索
            for (File subDir : subDirectories) {
                // 递归调用自身方法，处理下一个层级的目录
                // 这种递归调用会一直持续到没有更多子目录为止
                searchDocxFiles(subDir);
            }
        }
    }
}